/*!
 * @file system_LPC82x.c
 * @author suyong_yq@126.com
 */
#include <stdint.h>
#include "LPC82x.h"

/*----------------------------------------------------------------------------
  Clock Variable definitions
 *----------------------------------------------------------------------------*/
uint32_t SystemCoreClock = 30000000UL; /* System/Core Clock = 30MHz. */

/*----------------------------------------------------------------------------
  SystemInit
 *----------------------------------------------------------------------------*/
/*
 * 配置时钟系统
 * - 使用外部12MHz晶振作为PLL的输入时钟源
 * - 经过PLL倍频输出60MHz的Main clock
 * - 然后经过2分频产生30MHz的Core/Bus/Memory Clock
 */
void SystemInit(void)
{
    uint32_t i;
    /* 启用访问SWM和IOCON模块的时钟. 其中:
     * - SWM模块将用于通过引脚接入晶振信号到芯片内部
     * - IOCON模块将用于配置时钟系统
     */
    LPC_SYSCON->SYSAHBCLKCTRL |= SYSCON_SYSAHBCLKCTRL_SWM_MASK    /* Enables SWM clock.*/
                               | SYSCON_SYSAHBCLKCTRL_IOCON_MASK; /* Enables IOCON clock. */
    /* 配置引脚引入晶振信号 */
    LPC_IOCON->PIO0_8         &= ~(3 <<  3);          /* no pull-down/pull-up       */
    LPC_IOCON->PIO0_9         &= ~(3 <<  3);          /* no pull-down/pull-up       */
    LPC_SWM->PINENABLE0       &= ~(3 <<  6);          /* enable XTALIN/XTALOUT func.*/

    /* 配置外部晶振 */
    LPC_SYSCON->SYSOSCCTRL    = 0U; /* 不使用Bypass模式，晶振频率在1-20MHz区间 */
    LPC_SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSOSC_PD_MASK; /* 为SYSOSC电路上电 */
    /* Crystal oscillator power down. After power-up, add a software delay of approximately 500 μs 
     * before using. UM10800, P54, Table 54 Power configuration register bit description. */
    for (i = 0U; i < 1000U; i++) /* Wait for OSC to stabilize  */
    {
        __NOP();
    }

    /* 配置PLL */
    /* Select PLL Input.
     * 0x0 IRC
     * 0x1 Crystal Oscillator (SYSOSC)
     * 0x2 Reserved.
     * 0x3 CLKIN. External clock input.
     */
    LPC_SYSCON->SYSPLLCLKSEL  = SYSCON_SYSPLLCLKSEL_SEL(1);
    LPC_SYSCON->SYSPLLCLKUEN  = 0; /* Toggle Update Register. */
    LPC_SYSCON->SYSPLLCLKUEN  = 1;
    while (!(LPC_SYSCON->SYSPLLCLKUEN & SYSCON_SYSPLLCLKUEN_ENA_MASK)) /* Wait Until Updated. */
    {
        __NOP();
    }

    /* 设定PLL分频值后为PLL电路上电 */
    /* PLL输出频率的计算公式为:
     * - F_clkout = F_clkin x M
     * - FCCO = 2 x P x F_clkout 
     * 其中:
     * - F_clkin为PLL选择时钟源, M对应(SYSCON_SYSPLLCTRL[MSEL]+1), P对应(2 x SYSCON_SYSPLLCTRL[PSEL]).
     * - F_clkout输出的频率只与M直接相关, 但选定的P值对应产生FCCO需满足在范围156MHz - 320MHz之中.
     * - PLL的输出时钟F_clkout最大频率不能超过100MHz, LPC82x的CM0+内核标称只能工作在30MHz以下
     * 寄存器配置值参考UM10800, P59, Table 57 PLL configuration examples.
     */
    LPC_SYSCON->SYSPLLCTRL = SYSCON_SYSPLLCTRL_MSEL(4) | SYSCON_SYSPLLCTRL_PSEL(1);
    LPC_SYSCON->PDRUNCFG &= ~SYSCON_PDRUNCFG_SYSPLL_PD_MASK; /* Power-up SYSPLL。 */
    while (!(LPC_SYSCON->SYSPLLSTAT & SYSCON_SYSPLLSTAT_LOCK_MASK)) /* Wait Until PLL Locked. */
    {
         __NOP();
    }

    /* 选择Main Clock的时钟源为PLL输出 */
    LPC_SYSCON->MAINCLKSEL    = SYSCON_MAINCLKSEL_SEL(3); /* Select SYSPLL output. */
    LPC_SYSCON->MAINCLKUEN    = 0; /* Toggle Update Register. */
    LPC_SYSCON->MAINCLKUEN    = 1;
    while (!(LPC_SYSCON->MAINCLKUEN & SYSCON_MAINCLKUEN_ENA_MASK)) /* Wait Until Updated. */
    {
        __NOP();
    } 

    /* 设定从Main Clcok到AHB系统总线时钟的分频值 */
    LPC_SYSCON->SYSAHBCLKDIV  = 2; /* AHB总线时钟为Main Clock的2分频 */
}

/* EOF. */
